﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Xml;

namespace OK.SCOM.Authoring.Tools.MPElementsExtract
{
	internal class MPElementTypeScript: MPElementTypeBase
	{

		#region private types
		private class NodeProcessingResult
		{
			internal string Content { get; set; }
			internal NodeType NodeType { get; set; }
			internal XmlNode Node { get; set; }
		}

		private enum NodeType { Nothing, ScriptName, ScriptBody, Script };

		internal enum ExtractionMode { ScriptsExtract, SubScriptsExtract }
		#endregion

		private NodeProcessingResult m_lastProcResult;
		private NodeProcessingResult m_lastName;
		private NodeProcessingResult m_lastBody;
		private Dictionary<string, string> m_scriptsDict;

		internal override string Name
		{
			get
			{
				return String.Format("Script ({0})", Mode);
			}
		}

		internal override bool SupportsEmbed
		{
			get { return (Mode == ExtractionMode.SubScriptsExtract) ? true : false; }
		}

		internal override bool SupportsExtractReplaceWithPlaceMarks
		{
			get 
			{
				return (Mode == ExtractionMode.SubScriptsExtract) ? true : false;
			}
		}

		internal ExtractionMode Mode { get; set; }

		internal MPElementTypeScript(string baseDirectory, string extractBaseDirectory)
			: base(baseDirectory, extractBaseDirectory)
		{
			m_lastProcResult = null;
			m_lastName = null;
			m_lastBody = null;
			m_scriptsDict = new Dictionary<string, string>();
			Mode = ExtractionMode.ScriptsExtract;
		}
		
		protected override void ProcessSingleNodeBeforeChildren(System.Xml.XmlNode node, ParseAction action)
		{
			m_lastProcResult = new NodeProcessingResult();
			m_lastProcResult.NodeType = NodeType.Nothing;
		}

		protected override void ProcessSingleNodeAfterChild(System.Xml.XmlNode node, ParseAction action)
		{
			if (m_lastProcResult != null && m_lastProcResult.NodeType == NodeType.ScriptName) m_lastName = m_lastProcResult;
			else if (m_lastProcResult != null && m_lastProcResult.NodeType == NodeType.ScriptBody) m_lastBody = m_lastProcResult;
		}

		protected override void ProcessSingleNodeAfterChildren(System.Xml.XmlNode node, ParseAction action)
		{
			if (action == ParseAction.ExtractNoPlaceMarks || action == ParseAction.ExtractReplaceWithPlaceMarks)
			{
				Extract(node, action);
			}
			else if (action == ParseAction.Embed && node.Name == "ScriptBody")
			{
				EmbedContent(node);
			}
		}

		private void Extract(XmlNode node, ParseAction action)
		{
			if (node.Name == "ScriptName")
			{
				m_lastProcResult.NodeType = NodeType.ScriptName;
				m_lastProcResult.Content = node.InnerText;
				m_lastProcResult.Node = node;
			}
			else if (node.Name == "ScriptBody")
			{
				m_lastProcResult.NodeType = NodeType.ScriptBody;
				m_lastProcResult.Content = node.InnerText;
				m_lastProcResult.Node = node;
			}

			if (m_lastName != null && m_lastBody != null)
			{
				//process script
				string scriptName = m_lastName.Content;
				string scriptBody = m_lastBody.Content;

				if ((scriptName.StartsWith("$") && scriptName.EndsWith("$")))
				{
					Console.WriteLine("Invalid script name: {0}", scriptName);
				}
				else
				{
					if (Mode == ExtractionMode.ScriptsExtract)
					{
						int scriptNo = 0;

						while (m_scriptsDict.ContainsKey(GenerateScriptName(scriptName, scriptNo)) &&
							m_scriptsDict[GenerateScriptName(scriptName, scriptNo)] != scriptBody) ++scriptNo;

						scriptName = GenerateScriptName(scriptName, scriptNo);

						if (!m_scriptsDict.ContainsKey(scriptName)) m_scriptsDict.Add(scriptName, scriptBody);
						Save(scriptBody, scriptName);
						Console.WriteLine("Script found: {0} ({1} bytes), saved as: {2}", m_lastName.Content, scriptBody.Length, scriptName);
					}
					else if (Mode == ExtractionMode.SubScriptsExtract)
					{
						string[] lines = scriptBody.Split(new String[] { "\r\n" }, StringSplitOptions.None);
						string subScriptBody = "";
						string scriptBodyNew = "";
						string subScriptName = scriptName;

						foreach (string line in lines)
						{
							if (line.Contains("#####"))
							{
								if (subScriptName.Length > 0 && subScriptBody.Length > 0)
								{
									string scriptNameNew = SaveSubScript(subScriptName, subScriptBody);
									if (scriptBodyNew.Length > 0) scriptBodyNew += "\r\n";
									scriptBodyNew += "{{" + scriptNameNew + "}}";
								}

								int idx = line.IndexOf("#####");
								subScriptName = line.Substring(idx + 5);
								subScriptBody = "";
							}

							if (subScriptName != "")
							{
								if (subScriptBody.Length > 0) subScriptBody += "\r\n";
								subScriptBody += line;
							}
						}

						if (subScriptName.Length > 0 && subScriptBody.Length > 0)
						{
							string scriptNameNew = SaveSubScript(subScriptName, subScriptBody);
							if (scriptBodyNew.Length > 0) scriptBodyNew += "\r\n";
							scriptBodyNew += "{{" + scriptNameNew + "}}";
						}

						if (action == ParseAction.ExtractReplaceWithPlaceMarks)
						{
							m_lastBody.Node.InnerText = scriptBodyNew;
						}

					}
				}
				// /process script

				m_lastName = null;
				m_lastBody = null;
			}
		}

		private string SaveSubScript(string subScriptName, string subScriptBody)
		{
			int scriptNo = 0;

			while (m_scriptsDict.ContainsKey(GenerateScriptName(subScriptName, scriptNo)) &&
				m_scriptsDict[GenerateScriptName(subScriptName, scriptNo)] != subScriptBody) ++scriptNo;

			subScriptName = GenerateScriptName(subScriptName, scriptNo);

			string scriptNameNew = Save(subScriptBody, subScriptName);

			Console.WriteLine("Script found: {0} ({1} bytes), saved as: {2}", subScriptName, subScriptBody.Length, scriptNameNew);

			return scriptNameNew;
		}

		private string GenerateScriptName(string n, int i)
		{
			string res = n;

			if (i > 0)
			{
				res = ((i < 10) ? "0" : "") + i.ToString() + "_" + res;
			}

			return res;
		}


	}
}
